#ifndef tokenizer_h__
#define tokenizer_h__

#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <list>
#include <stack>

namespace CCompiler
{
    enum TokenType
    {   ttNotToken,
        ttIdentifier,
        // delimeters
        ttDot, ttComma, ttSemicolon, ttColon, ttOpenParenthesis, ttCloseParenthesis, ttLeftBrace, ttRightBrace, ttOpenBracket, ttCloseBracket, ttEllipsis,
        // keywords
       /* ttInt, ttFloat, ttVoid,*/ ttReturn, ttStruct, ttFor, ttWhile, ttIf, ttSwitch, ttCase, ttElse, ttTypedef, ttAuto,
        ttStatic, ttExtern, ttSizeof, ttDo, ttDefault, ttBreak, ttContinue, ttPrintf, 
        // constants
        ttInteger, ttReal, ttString, ttChar,
        // operations
        ttAssign, ttEqual, ttPlus, ttMinus, ttBAnd, ttBOr, ttXor, ttAnd, ttOr, ttNot, ttPlusAssign, ttMinusAssign,
        ttAsterisk, ttMulAssign, ttDiv, ttDivAssign, ttMod, ttModAssign, ttInc, ttDecr, ttBAndAssign, ttBOrAssign,
        ttXorAssign, ttNotEqual, ttGreater, ttLess, ttGreaterEqual, ttLessEqual, ttArrow, ttShL, ttShR, ttQuestion,
        ttComplement, ttShLAssign, ttShRAssign, ttError, ttEof
    };
    static std::string TokenTypeStrAnalogs[] =
    {
        "NotToken",
        "Identificator",
        // delimeters
        "Dot", "Comma", "Semicolon", "Colon", "OpenParenthesis", "CloseParenthesis", "LeftBrace", "RightBrace", "OpenBracket", "CloseBracket", "Ellipsis",
        // keywords
        /*"Int", "Float", "Void",*/ "return", "struct", "for", "while", "if", "switch", "case", "else", "typedef", "auto",
        "static", "extern", "sizeof", "do", "default", "break", "continue", "printf",
        // constants
        "Integer", "Real", "String", "Char",
        // operations
        "Assign", "Equal", "Plus", "Minus", "BAnd", "BOr", "Xor", "And", "Or", "Not", "PlusAssign", "MinusAssign",
        "Asterisk", "MulAssign", "Div", "DivAssign", "Mod", "ModAssign", "Inc", "Decr", "BAndAssign", "BOrAssign",
        "XorAssign", "NotEqual", "Greater", "Less", "GreaterEqual", "LessEqual", "Arrow", "ShL", "ShR", "Question",
        "Complement", "ShLAssign", "ShRAssign", "ERROR", "EOF"
    };
	std::string StringAnalogOfTokenTypeOperation(TokenType tt);
	class Token
    {
        TokenType type_;
        std::string text_;
        union
        {
            int i_;
            float f_;
            char c_;
        } value_;
        std::string strvalue_;
        size_t line_, pos_;
		friend class Tokenizer;
    public:
        Token(const TokenType type = ttNotToken, const std::string &text = "", const size_t line = 0, const size_t pos = 0):
		  type_(type), text_(text), line_(line), pos_(pos)
        { value_.f_ = 0;}
		Token(const TokenType type, const std::string &text, const size_t line, const size_t pos, int vali):
		type_(type), text_(text), line_(line), pos_(pos)
		{ value_.i_ = vali;}
		Token(const TokenType type, const std::string &text, const size_t line, const size_t pos, float valf):
		type_(type), text_(text), line_(line), pos_(pos)
		{ value_.f_ = valf;}
		Token(const TokenType type, const std::string &text, const size_t line, const size_t pos, char valc):
		type_(type), text_(text), line_(line), pos_(pos)
		{ value_.c_ = valc;}
		Token(const TokenType type, const std::string &text, const size_t line, const size_t pos, const std::string &valstr):
		type_(type), text_(text), strvalue_(valstr), line_(line), pos_(pos)
		{ value_.f_ = 0;}
		TokenType getTokenType() const { return type_; }
		int getIntValue() const { return value_.i_; }
		float getFloatValue() const { return value_.f_; }
		char getCharValue() const { return value_.c_; }
		std::string getStringValue() const { return strvalue_; }
		const std::string &getText() const { return text_; }
        size_t getLine() { return line_; }
        size_t getPos() { return pos_; }
        friend std::ostream &operator<<(std::ostream& stream, const Token &token);
    };

    enum FAS
    {
        fasError=0, fasStart, fasId, fasZero, fasOct, fasHex, fasDec, fasHexPrefix, fasDecwP, fasFloat, fasFwE,
        fasFwES, fasFloatwExp, fasDotDot, fas2Q, fas2QandC, fasString, fas1QandC, fasChar,
        fas1Q,
		// starting from Dot, ending at WhiteSpace, put here only simple tokens
        fasDot, fasEllipsis, fasComma, fasSemicolon, fasColon, fasOpenParenthesis, fasCloseParenthesis, fasLeftBrace, fasRightBrace,
        fasOpenBracket, fasCloseBracket, fasAssign, fasEqual, fasPlus, fasMinus, fasBAnd, fasBOr, fasXor, fasAnd, fasOr, fasNot,
        fasPlusAssign, fasMinusAssign, fasAsterisk, fasMulAssign, fasDiv, fasDivAssign, fasMod, fasModAssign, fasInc,
		fasDecr, fasBAndAssign, fasBOrAssign, fasXorAssign, fasNotEqual, fasGreater, fasLess, fasGreaterEqual,
		fasLessEqual, fasArrow, fasShL, fasShR, fasQuestion, fasComplement, fasShLAssign, fasShRAssign,
		// simple tokens ends here
        // starting from WhiteSpace, ending at MLCommentOpen, this states must be ignored
		fasWhiteSpace,
		fasCRLF, fasLComment, fasMLComment,
		// ignoring states ends here
        fasMLCommentOpen, fasMLCommentBody, fasMLCommentStar, fasEof, fas1QandSlash,
		fas1QandSimpleTerminal, fas1QandOct, fas1QandHex, fasCharWithHex, fasCharWithOct,
		fasCharWithSimpleTerminal, fas2QandSlash, fasEnd
    };
    class FiniteAutomation
    {

        char ch_;
        std::basic_istream<char> &stream_;
        size_t line_, pos_;
        size_t posInLine_;
    public:
        FiniteAutomation(std::basic_istream<char>& stream);
        FAS state() { return state_; }
        size_t line() { return line_; }
        size_t pos() { return pos_; }
        char lastReadCh() { return ch_; }
        void launch();
        std::string processed_;
    private:
        FiniteAutomation &operator=(const FiniteAutomation&op);
        FAS state_;
    };

	class Tokenizer
	{
		std::istream &stream_;
        std::stack<Token> buff;
		FiniteAutomation fa;
		size_t current_;
		Token token_;
	public:
		Tokenizer(std::istream &stream);
		Token getCurToken();
		Token nextToken();
        void putBack(Token tok);
	};
}

namespace __InitializeTokenizer { void initialize(); }

#endif // tokenizer_h__

